<?php
/**
 * Created by PhpStorm.
 * User: happy
 * Date: 19/4/22
 * Time: 下午2:29
 * 执行swoole 启动以及服务器事件关联等事件
 */
//用于全局获取服务器对象和服务器类型

abstract class TaskSwoole {

    public static function add($args){
        return Server::$instance->task($args);
    }

    public static function onTask(swoole_server $server, int $taskID, int $workerID, $args) {
        call_user_func_array($args['callback'], $args['param']);
        $server->finish($taskID);
    }

    public static function onFinish(swoole_server $server, int $taskID, string $data){
        Logger::log(__METHOD__.' taskID => '.$taskID.' finish');
    }
}
//原来的hooker 类
class EventSwoole {

    public  static  $after_worker_start;
    public  static  $request_callback;//worker进程回调之前
    public  static $receive_callback;
    public  static $task_callback;
    public  static  $connect_callback;

    // Manager start
    public static function onManagerStart(swoole_server $server){
        swoole_set_process_name('swoole_manager');
    }

    // Worker start
    public static function onWorkerStart(swoole_server $server, int $workerID){

        if ($server->taskworker) {//是否 Task 工作进程
            $process_name = 'swoole_task'.$workerID;
        }else{
            $process_name = 'swoole_worker'.$workerID;
        }
        swoole_set_process_name($process_name);
        $after_worker_start=self::$after_worker_start;
        if($after_worker_start){
            $after_worker_start($server, $workerID);//worker 启动回调事件
        }
        echo chr(10).'worker start:'.$workerID.' coroutine_id:'.Co::getuid();
       // Logger::log('Worker '.$workerID.' ready for connections ...');
    }

    // Http onRequest, 将请求路由至控制器
    public static function onRequest(swoole_http_request $request, swoole_http_response $response){

        if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') {
             $response->end();return;
        }
        $method = strtoupper($request->server['request_method']);
        $request_callback=  self::$request_callback;
        if($request_callback){
            $request_callback($method, $request, $response);
        }
        else{
            self::default_request($method, $request, $response);
        }
    }

    public  static  function default_request($method, $request, $response){
        $response->header("Content-Type", "text/html; charset=utf-8");

        $server=$request->server;
        $request_url=trim($server['request_uri'], '/');

        $path_arr=explode('/',$request_url);
        if(count($path_arr)>1){//==1是访问根目录,大于1是访问子目录app
            $app_name=$path_arr[0];
            define('APP_NAME',$app_name);
            $base_path=ROOT_PATH.'/'.$app_name;
            define('BASE_PATH',$base_path);//定义基础目录

            //改变当前目录为app主目录
            if(file_exists($base_path)){
                chdir($base_path);
            }else{
                return  $response->end('child dir:'.$app_name.' not exits');
            }
        }
        else{
            return  $response->end('visit root');
        }

        $base_index_path=$base_path.'/index.php';
        if(file_exists($base_index_path)){
            include ($base_index_path);
        }
        //初始化app 内自定义配置
        $GET=$request->get;
        $POST=$request->post;
        $act= $GET['act'] ? strtolower($GET['act']) : ($POST['act'] ? strtolower($POST['act']) : null);
        $op = $GET['op'] ? strtolower($GET['op']) : ($POST['op'] ? strtolower($POST['op']) : null);

        $act = $act ?  $act : 'index';
        $op=$op? $op : 'index';

        $act_file =BASE_PATH.'/control/'.$act.'.php';
        $class_name = $act.'Control';

        if (!class_exists($class_name,false)){
            if(file_exists($act_file)){
                include($act_file);
                //加载后文件依然不存在
                if(!class_exists($class_name,false)){
                    $error = "Base Error: class $class_name isn't exists!";
                    return $response->end($error);
                }
            }
            else{
                return $response->end("Base Error: $act_file access file isn't exists!");
            }
        }
        $main = new $class_name($request,$response);

        $main->method=$method;//访问方法,get  post等


        $function = $op;
        //定义act和op
        if (method_exists($main,$function)){
            $main->$function();
            $main->end();//继承control，无论是否输出都调用一下end结束本次请求
        }else {
            $error = "Base Error: function $function not in $class_name!";
            return $response->end($error);
        }

    }

    // TCP onConnect
    public static function onConnect(swoole_server $server, int $fd, int $reactorID){
        $connect_callback=self::$connect_callback;
        if($connect_callback){
            $connect_callback($server, $fd, $reactorID);
        }
    }

    // TCP onReceive
    public static function onReceive(swoole_server $server, int $fd, int $reactorID, string $json){
        $receive_callback=self::$receive_callback;
        if($receive_callback){
            $receive_callback($server, $fd, $reactorID);
        }
        // 分包
        $data_list = explode("\r\n", $json);
        if($data_list){
            foreach($data_list as $msg){
                $data = json_decode($msg, TRUE);
                if($data){
                    $module = trim($data['module']);
                    if(!$module){
                        $module = 'index';
                    }

                    $controller = trim($data['controller']);
                    if($controller){
                        $instance = Helper::import($module, $controller);

                        if($instance !== FALSE){
                            $instance->fd     = $fd;
                            $instance->data   = $data;
                            $instance->server = $server;

                            $action = trim($data['action']);
                            !$action && $action = 'index';
                            $instance->$action();
                        }else{
                            $rep['code']  = 0;
                            $rep['error'] = 'Controller '.$controller.' not found';
                            $server->send($fd, JSON($rep));
                        }
                    }
                }
            }
        }
    }

    // UDP onPacket
    public static function onPacket(swoole_server $server, string $json, array $client){
        //Worker::beforePacket($server, $json, $client);
        $data = json_decode($json, TRUE);
        if(!$data){
            $rep['code']  = 0;
            $rep['error'] = 'Not valid JSON';
            $server->sendto($client['address'], $client['port'], JSON($rep));
        }else{
            $module = trim($data['module']);
            if(!$module){
                $module = 'index';
            }

            $controller = trim($data['controller']);
            if($controller){
                $instance = Helper::import($module, $controller);

                if($instance !== FALSE){
                    $instance->data   = $data;
                    $instance->server = $server;
                    $instance->client = $client;

                    $action = trim($data['action']);
                    !$action && $action = 'index';
                    $instance->$action();
                }else{
                    $rep['code']  = 0;
                    $rep['error'] = 'Controller '.$controller.' not found';
                    $server->sendto($client['address'], $client['port'], JSON($rep));
                }
            }
        }
    }

    // Websocket onOpen
    public static function onOpen(swoole_websocket_server $server, swoole_http_request $request){
        //Worker::afterOpen($server, $request);
       // Logger::log('Client '.$request->fd.' connected');
    }

    // Websocket onMessage
    public static function onMessage(swoole_websocket_server $server, swoole_websocket_frame $frame){
      //  Worker::beforeMessage($server, $frame);
        $data = json_decode($frame->data, 1);
        if(!$data){
            $rep['code']  = 0;
            $rep['error'] = 'Not valid JSON';
            $server->push($frame->fd, JSON($rep));
        }else{
            $module = trim($data['module']);
            if(!$module){
                $module = 'index';
            }

            $controller = trim($data['controller']);
            if($controller){
                $instance = Helper::import($module, $controller);

                if($instance !== FALSE){
                    $instance->data   = $data;
                    $instance->server = $server;
                    $instance->fd     = $frame->fd;

                    $action = trim($data['action']);
                    !$action && $action = 'index';
                    $instance->$action();
                }else{
                    $rep['code']  = 0;
                    $rep['error'] = 'Controller '.$controller.' not found';
                    $server->push($frame->fd, JSON($rep));
                }
            }
        }
    }

    // onClose
    public static function onClose(swoole_server $server, int $fd, int $reactorID){
       // Worker::afterClose($server, $fd, $reactorID);
       // Logger::log('Client '.$fd.' closed');
    }

    // Worker error
    public static function onWorkerError(swoole_server $serv, int $workerID, int $workerPID, int $exitCode, int $signal){

       //Logger::fatal('Worker '.$workerID.' exit with code '.$exitCode.' and signal '.$signal);
    }

    // Worker stop
    public static function onWorkerStop(swoole_server $server, int $workerID){
       // Worker::afterStop($server, $workerID);
      //  Logger::log('Worker '.$workerID.' stop');
    }

}

//用于control 中引用和获取server对象,静态类
abstract class server {

    public static $type;
    public static $instance;

    const TYPE_TCP        = 'tcp';
    const TYPE_UDP        = 'udp';
    const TYPE_HTTP       = 'http';
    const TYPE_WEB_SOCKET = 'websocket';
    const SOFT_NAME         ='swoole_eventphp';
}


class tcp_server {

    private $server;

    public function __construct($config) {

        $ip   = $config['host'];
        $port = $config['port'];

        $this->server = new Swoole_Server($ip, $port);

        $this->server->set($config);
        $this->server->on('task',         ['TaskSwoole',   'onTask']);
        $this->server->on('finish',       ['TaskSwoole',   'onFinish']);
        $this->server->on('Close',        ['EventSwoole', 'onClose']);
        $this->server->on('Connect',      ['EventSwoole', 'onConnect']);
        $this->server->on('Receive',      ['EventSwoole', 'onReceive']);
        $this->server->on('WorkerStop',   ['EventSwoole', 'onWorkerStop']);
        $this->server->on('WorkerError',  ['EventSwoole', 'onWorkerError']);
        $this->server->on('WorkerStart',  ['EventSwoole', 'onWorkerStart']);
        $this->server->on('ManagerStart', ['EventSwoole', 'onManagerStart']);

    }

    public function start() {
        server::$type = server::TYPE_TCP;
        server::$instance = $this->server;

        swoole_set_process_name('tcp_swoole_master');
        $this->server->start();
    }
}

class websocket_server {

    private $server;

    public function __construct($config) {

        $ip   = $config['host'];
        $port = $config['port'];

        $this->server = new swoole_websocket_server($ip, $port);


        $this->server->set($config);
        $this->server->on('task',          ['TaskSwoole',   'onTask']);
        $this->server->on('finish',        ['TaskSwoole',   'onFinish']);
        $this->server->on('open',          ['EventSwoole', 'onOpen']);
        $this->server->on('close',         ['EventSwoole', 'onClose']);
        $this->server->on('message',       ['EventSwoole', 'onMessage']);
        $this->server->on('WorkerStop',    ['EventSwoole', 'onWorkerStop']);
        $this->server->on('WorkerError',   ['EventSwoole', 'onWorkerError']);
        $this->server->on('WorkerStart',   ['EventSwoole', 'onWorkerStart']);
        $this->server->on('ManagerStart',  ['EventSwoole', 'onManagerStart']);

    }

    public function start() {
        server::$instance = $this->server;
        server::$type = Server::TYPE_WEB_SOCKET;

        swoole_set_process_name('websocket_swoole_master');
        Logger::log('======= Websocket master start =======');

        $this->server->start();
    }
}

class http_server {

    private $server;

    public function __construct($config) {

        $ip   = $config['host'];
        $port = $config['port'];

        $this->server = new swoole_http_server($ip, $port);

        $this->server->set($config);
        $this->server->on('task',         ['TaskSwoole',   'onTask']);
        $this->server->on('finish',       ['TaskSwoole',   'onFinish']);
        $this->server->on('request',      ['EventSwoole', 'onRequest']);
        $this->server->on('WorkerStop',   ['EventSwoole', 'onWorkerStop']);
        $this->server->on('WorkerError',  ['EventSwoole', 'onWorkerError']);
        $this->server->on('WorkerStart',  ['EventSwoole', 'onWorkerStart']);
        $this->server->on('ManagerStart', ['EventSwoole', 'onManagerStart']);
    }

    public function start() {
        server::$type = Server::TYPE_HTTP;
        server::$instance = $this->server;

        swoole_set_process_name('http_swoole_master');
        //Logger::log('======= HTTP master start =======');
        $this->server->start();
    }
}

class udp_server {

    private $server;

    public function __construct($config) {
        $ip   = $config['host'];
        $port = $config['port'];

        $this->server = new Swoole_Server($ip, $port, SWOOLE_PROCESS, SWOOLE_SOCK_UDP);
        $this->server->set($config);
        $this->server->on('task',         ['Task',   'onTask']);
        $this->server->on('finish',       ['Task',   'onFinish']);
        $this->server->on('Packet',       ['Hooker', 'onPacket']);
        $this->server->on('WorkerStop',   ['Hooker', 'onWorkerStop']);
        $this->server->on('WorkerError',  ['Hooker', 'onWorkerError']);
        $this->server->on('WorkerStart',  ['Hooker', 'onWorkerStart']);
        $this->server->on('ManagerStart', ['Hooker', 'onManagerStart']);

    }

    public function start() {
        server::$type = server::TYPE_UDP;
        server::$instance = $this->server;

        swoole_set_process_name('udp_swoole_master');
        //Logger::log('======= UDP master start =======');

        $this->server->start();
    }
}


